package com.alibaba.csp.sentinel.dashboard.rule.convert;

import com.alibaba.csp.sentinel.adapter.gateway.common.api.ApiDefinition;
import com.alibaba.csp.sentinel.adapter.gateway.common.api.ApiPathPredicateItem;
import com.alibaba.csp.sentinel.adapter.gateway.common.api.ApiPredicateGroupItem;
import com.alibaba.csp.sentinel.adapter.gateway.common.api.ApiPredicateItem;
import com.alibaba.csp.sentinel.adapter.gateway.common.rule.GatewayFlowRule;
import com.alibaba.csp.sentinel.dashboard.datasource.entity.gateway.ApiDefinitionEntity;
import com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.RuleEntity;
import com.alibaba.csp.sentinel.dashboard.discovery.AppInfo;
import com.alibaba.csp.sentinel.dashboard.discovery.MachineInfo;
import com.alibaba.csp.sentinel.dashboard.rule.convert.adapter.JsonConverter;
import com.alibaba.csp.sentinel.datasource.Converter;
import com.alibaba.fastjson.JSON;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.Version;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.node.ObjectNode;
import org.springframework.context.annotation.Bean;

import java.io.IOException;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author: shaozeming
 * @date: 2022/8/30 9:52
 * @description:
 **/
public class GatewayApiConverter implements RuleConverter {

    private static ObjectMapper objectMapper = new ObjectMapper();
    private static  JsonConverter<ApiDefinition> jsonConverter= null;
    static {


        objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES,
                false);

        ApiPredicateItemDeserializer deserializer = new ApiPredicateItemDeserializer();
        deserializer.registerApiPredicateItem("pattern",
                ApiPathPredicateItem.class);
        deserializer.registerApiPredicateItem("items",
                ApiPredicateGroupItem.class);
        SimpleModule module = new SimpleModule(
                "PolymorphicApiPredicateItemDeserializerModule",
                new Version(1, 0, 0, null));
        module.addDeserializer(ApiPredicateItem.class, deserializer);
        objectMapper.registerModule(module);
        jsonConverter= new JsonConverter<>(objectMapper, ApiDefinition.class);
    }


    @Override
    public Converter<List<RuleEntity>, String> ruleEntityEncoder() {
        return v -> JSON.toJSONString(v.stream().map(entity -> ((ApiDefinitionEntity) entity).toApiDefinition()).collect(Collectors.toList()));
    }

    @Override
    public Converter<String, List<RuleEntity>> ruleEntityDecoder(AppInfo appInfo) {
        ArrayList<MachineInfo> machineList = new ArrayList<>(appInfo.getMachines());
        return s -> Optional.ofNullable(s)
                .map(this::parseEntity)
                .map(rules -> rules.stream()
                        .map(e -> (RuleEntity) ApiDefinitionEntity.fromApiDefinition(appInfo.getApp(), machineList.get(0).getIp(), machineList.get(0).getPort(), e))
                        .collect(Collectors.toList()))
                .orElse(null);
    }



    private Collection<ApiDefinition> parseEntity(String rule) {
        return jsonConverter.convert(rule);
    }

    static class ApiPredicateItemDeserializer
            extends StdDeserializer<ApiPredicateItem> {

        private Map<String, Class<? extends ApiPredicateItem>> registry = new HashMap<String, Class<? extends ApiPredicateItem>>();

        ApiPredicateItemDeserializer() {
            super(ApiPredicateItem.class);
        }

        void registerApiPredicateItem(String uniqueAttribute,
                                      Class<? extends ApiPredicateItem> apiPredicateItemClass) {
            registry.put(uniqueAttribute, apiPredicateItemClass);
        }

        @Override
        public ApiPredicateItem deserialize(JsonParser jp,
                                            DeserializationContext ctxt) throws IOException {
            ObjectMapper mapper = (ObjectMapper) jp.getCodec();
            ObjectNode root = mapper.readTree(jp);
            Class<? extends ApiPredicateItem> apiPredicateItemClass = null;
            Iterator<Map.Entry<String, JsonNode>> elementsIterator = root.fields();
            while (elementsIterator.hasNext()) {
                Map.Entry<String, JsonNode> element = elementsIterator.next();
                String name = element.getKey();
                if (registry.containsKey(name)) {
                    apiPredicateItemClass = registry.get(name);
                    break;
                }
            }
            if (apiPredicateItemClass == null) {
                return null;
            }
            return mapper.readValue(root.toString(), apiPredicateItemClass);
        }
    }
}
